/*--------------------------------------------------------------------------
    This source file is part of Abraxas Game Engine
    For the latest info, see http://code.google.com/abraxasge/
    Copyright (C) 2009, Andrew LeRoy <aleroy@member.igda.org>
    ---

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA  02110-1301, USA.
--------------------------------------------------------------------------*/
#include "AbxEngine.hpp"
#include "AbxStdHeaders.hpp"

#include "AbxLogManager.hpp"
#include "AbxOptions.hpp"
#include "AbxException.hpp"
#include "AbxDynamicLibrary.hpp"
#include "AbxPlugin.hpp"
#include "AbxPluginManager.hpp"
#include "AbxGraphicsSystem.hpp"


#include <boost/program_options/parsers.hpp>

namespace Abx
{
    typedef void (*DLL_START_PLUGIN)(void);
    typedef void (*DLL_STOP_PLUGIN)(void);

    EngineImpl::EngineImpl()
        : created_(true), defaultGfx_(NULL)
    { }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    EngineImpl::~EngineImpl()
    {
        if (defaultGfx_) { delete defaultGfx_; }
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯



#if ABX_PLATFORM == ABX_PLATFORM_WIN32
    std::vector<String> EngineImpl::getArgs(int argc, String const& lpCmdLine)
    {
        std::vector<String> args = boost::program_options::split_winmain(lpCmdLine);
        return args;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#endif

    std::vector<String> EngineImpl::getArgs(int argc, char* argv[])
    {
        std::vector<String> args;
        for (int i = 0; i < argc; ++i)
        {
            args.push_back(argv[i]);
        }
        return args;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    bool EngineImpl::startup(StringVector args)
    {
        // Startup Program Options; return on error
        if (!Options::Instance().startup(args))
        { return false; }

        // Startup LogManager
        LogManager::Instance().startup();


        // Register default systems
        defaultGfx_ = new DefaultGraphicsSystemFactory;
        gfx_.Register(defaultGfx_);
        gfx_.Create("DefaultGraphicsSystem", "sys_default_gfx");


        loadPlugins();
        startupPlugins();
        
        return true;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    bool EngineImpl::shutdown()
    {
        shutdownPlugins();
        unloadPlugins();

        // Unregister default systems
        gfx_.Unregister(defaultGfx_);

        LogManager::Instance().shutdown();
        Options::Instance().shutdown();

        return true;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    bool EngineImpl::update()
    {
        return true;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    bool EngineImpl::run()
    {
        return true;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::loadPlugins()
    {
        if (Options::Instance().empty("Engine.Plugin"))
        { return; }

        String path;
        StringVector names;
        path = Options::Instance().getOption<String>("Engine.PluginPath");
        names = Options::Instance().getOption<std::vector<String> >("Engine.Plugin");
#if ABX_PLATFORM != ABX_PLATFORM_APPLE
        if (path.empty())
        {
            // User didn't specify plugins folder, try current one
            path = ".";
        }
#endif
        char last_char = path[path.length()-1];
        if (last_char != '/' && last_char != '\\')
        {
#if ABX_PLATFORM == ABX_PLATFORM_WIN32
            path += "\\";
#elif ABX_PLATFORM == ABX_PLATFORM_LINUX
            path += "/";
#endif
        }

        for (StringVector::iterator it = names.begin(); it != names.end(); ++it)
        {
            loadPlugin(path + (*it));
        }
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::startupPlugins()
    {
        for (Plugins::iterator it = plugins_.begin(); it != plugins_.end(); ++it)
        {
            (*it)->startup();
        }
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::shutdownPlugins()
    {
        for (Plugins::reverse_iterator rit = plugins_.rbegin(); rit != plugins_.rend(); ++rit)
        {
            (*rit)->shutdown();
        }
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::unloadPlugins()
    {
        for (PluginLibs::reverse_iterator rit = pluginLibs_.rbegin(); rit != pluginLibs_.rend(); ++rit)
        {
            DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)(*rit)->getSymbol("dllStopPlugin");
            pFunc();
            PluginManager::Instance().unload(*rit);
        }
        pluginLibs_.clear();

        for (Plugins::reverse_iterator rit = plugins_.rbegin(); rit != plugins_.rend(); ++rit)
        {
            (*rit)->uninstall();
        }
        plugins_.clear();
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    void EngineImpl::loadPlugin(String const& pluginName)
    {
        DynamicLibrary* lib = PluginManager::Instance().load(pluginName);
        pluginLibs_.push_back(lib);

        DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->getSymbol("dllStartPlugin");
        if (!pFunc)
        {
            ABX_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
                       "Cannot find symbol dllStartPlugin in library " + pluginName,
                       "Engine::loadPlugin");
        }
        pFunc();
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::unloadPlugin(String const& pluginName)
    {
        for (PluginLibs::iterator it = pluginLibs_.begin(); it != pluginLibs_.end(); ++it)
        {
            if ((*it)->getName() == pluginName)
            {
                DLL_STOP_PLUGIN pFunc = (DLL_STOP_PLUGIN)(*it)->getSymbol("dllStopPlugin");
                pFunc();

                PluginManager::Instance().unload(*it);
                pluginLibs_.erase(it);
                return;
            }
        }
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    EngineImpl::Plugins const& EngineImpl::getInstalledPlugins() const
    {
        return plugins_;
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::registerPlugin(Plugin* plugin)
    {
        LogManager::Instance().logMessage("Installing plugin: " + plugin->getName());

        plugins_.push_back(plugin);
        plugin->install();
        plugin->startup();

        LogManager::Instance().logMessage("Plugin successfully installed");
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::unregisterPlugin(Plugin* plugin)
    {
        LogManager::Instance().logMessage("Uninstalling plugin: " + plugin->getName());

        Plugins::iterator it = std::find(plugins_.begin(), plugins_.end(), plugin);
        if (it != plugins_.end())
        {
            plugin->shutdown();
            plugin->uninstall();
            plugins_.erase(it);
        }
        LogManager::Instance().logMessage("Plugin successfully uninstalled");
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::registerSystem(GraphicsSystemFactory* gfxFactory)
    {
        gfx_.Register(gfxFactory);
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
    void EngineImpl::unregisterSystem(GraphicsSystemFactory* gfxFactory)
    {
        gfx_.Unregister(gfxFactory);
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    GraphicsSystem* EngineImpl::getSystem(String const& name, bool byTypeName) const
    {
        return gfx_.getInstance(name, byTypeName);
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    bool EngineImpl::isCreated() const
    {
        return (created_);
    }
    //¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

} // namespace abx
